use std::{
    env,
    fs::{self, File},
    io::Write,
    path::PathBuf,
    sync::LazyLock,
};

use chrono::Local;
use pdf_extract::extract_text;
use regex::Regex;
use serde::Deserialize;

static APP_CONFIG: LazyLock<AppConfig> = LazyLock::new(|| read_config());

pub fn start() -> Result<(), Box<dyn std::error::Error>> {
    let exe_path = env::current_exe()?.parent().unwrap().to_owned();

    println!("Starting in {:?}", &exe_path);

    for entry in fs::read_dir(&exe_path)? {
        let entry = entry?;
        let path = entry.path();

        let filename = path
            .file_name()
            .unwrap_or_default()
            .to_str()
            .unwrap_or_default();

        let extension = path
            .extension()
            .unwrap_or_default()
            .to_str()
            .unwrap_or_default();

        let pattern = format!(
            r"(?s){}.+?{}",
            &APP_CONFIG.match_pattern.start, &APP_CONFIG.match_pattern.end
        );

        let re = Regex::new(&pattern).unwrap();

        if path.is_file() && extension.eq_ignore_ascii_case("pdf") {
            println!("Reading {} ...", filename);

            let content_text = extract_text(&path)?;
            save_file(re, &exe_path, &path, &content_text).unwrap();
        }
    }

    println!("Done");

    Ok(())
}

#[derive(Debug, Deserialize)]
struct AppConfig {
    #[serde(rename(deserialize = "match-pattern"))]
    match_pattern: MatchPattern,
}

#[derive(Debug, Deserialize)]
struct MatchPattern {
    start: String,
    end: String,
}

fn save_file(
    re: Regex,
    exe_path: &PathBuf,
    path: &PathBuf,
    content_text: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let new_filename = format!(
        "{}-{}.txt",
        path.file_name()
            .unwrap_or_default()
            .to_str()
            .unwrap_or_default()
            .strip_suffix(".pdf")
            .unwrap_or_default()
            .to_owned(),
        Local::now().format("%Y%m%d-%H%M%S")
    );

    println!("Processing ...");

    let result = re.replace_all(content_text, &APP_CONFIG.match_pattern.end);

    println!("Creating {} ...", new_filename);

    let mut file = File::create(&exe_path.join(&new_filename))?;
    file.write_all(result.as_bytes())?;

    println!("{} created", &new_filename);

    Ok(())
}

fn read_config() -> AppConfig {
    let exe_path = env::current_exe().unwrap().parent().unwrap().to_owned();
    let config_file_path = exe_path.join("config.yml");

    let config_str = std::fs::read_to_string(&config_file_path)
        .expect(format!("Failed to read config file '{:?}'", &config_file_path).as_str());

    let config = serde_yaml::from_str(&config_str).expect("Failed to parse config file");

    config
}
